package com.devinsor.spring.framework.beans.support;

import com.devinsor.spring.framework.beans.config.BeanDefinition;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @program: com.elvis.demo-v1
 * @description: 用来读取配置文件的工具类
 * @author: JackRen
 * @create: 2020-05-13 14:58
 **/
public class BeanDefinitionReader {

    private Properties contextConfig=new Properties();

    private List<String> registerBeanClasses = new ArrayList<String>();

    public BeanDefinitionReader(String[] configLocations) {
        //1.读取配置文件
        doLoadConfig(configLocations[0]);

        //2.扫描相关类
        doScanner(contextConfig.getProperty("scanPackage"));
        
    }

    /**
      * @author JackRen
      * @date 2020/5/13
     * 开始扫描
    */
    private void doScanner(String scanPackage) {
        //去ClassPath下找到相关的.class文件
        //scanPackage包路径，就是对应文件夹
        URL url = this.getClass().getClassLoader().getResource("/"+ scanPackage.replaceAll("\\.","/"));

        File classPath = new File(url.getFile());
        for (File file: classPath.listFiles()) {
            if (file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else {
                //classPath下除了有.class文件，还有 .xml  .properties .yml
                //取反，减少代码嵌套
                //代码嵌套超过三层，就要拉平
                if (!file.getName().endsWith(".class")){continue;}
                String className = scanPackage + "." + file.getName().replaceAll(".class","");
                //            可以在实例化阶段通过调用Class.forName(className)拿到Class对象
                //从而可以通过反射去创建实例
                registerBeanClasses.add(className);
            }
        }

    }

    /**
      * @author JackRen
      * @date 2020/5/13
     * 读取配置文件
    */
    private void doLoadConfig(String configLocation) {
        //直接从ClassPath下去找到Spring的配置文件
        //相当于把application.properties 文件读到了内存中
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation.replaceAll("classpath:",""));
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (is!=null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**  
      * @author JackRen 
      * @date 2020/5/13   
     * 字符串的第一个大写字母小写
    */
    private String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        //因为大写字符的ASCII码和小写字母的ASCII正好相差32
        //而且大写字母ASCII码要小于小写字母的ASCII码
        chars[0] += 32;
        return String.valueOf(chars);
    }

    public List<BeanDefinition> loadBeanDefinitions() {
        List<BeanDefinition> result = new ArrayList<BeanDefinition>();
        try {
            for (String className:registerBeanClasses) {
                Class<?> beanClass = Class.forName(className);
                //判断自身是不是一个接口
                //如果扫描到的类是demo.service.IQueryService就continue
                //如果扫描到的类是demo.service.impl.ModifyService就继续往下走
                if (beanClass.isInterface()){continue;}

                //com.elvis.demo.service.impl.ModifyService
                // key = modifyService,value=com.elvis.demo.service.impl.ModifyService
                result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()),beanClass.getName()));

                for (Class<?> i:beanClass.getInterfaces()){
                    // key = com.elvis.demo.service.IQueryService,value=com.elvis.demo.service.impl.ModifyService
                    result.add(doCreateBeanDefinition(i.getName(),beanClass.getName()));
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
       return  result;
    }

    private BeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName) {
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setFactoryBeanName(factoryBeanName);
        beanDefinition.setBeanClassName(beanClassName);
        return beanDefinition;
    }

    public Properties getConfig() {
        return this.contextConfig;
    }
}
